package com.m3u8;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;

import com.m3u8.bean.M3U8;
import com.m3u8.bean.M3U8Ts;
import com.m3u8.db.DbAction;
import com.m3u8.db.DbCallback;
import com.m3u8.db.DownloadM3u8DB;
import com.m3u8.db.M3u8Dao;
import com.m3u8.db.TsDao;
import com.m3u8.db.model.M3u8Model;
import com.m3u8.db.model.TsModel;
import com.m3u8.http.OKhttper;
import com.m3u8.utils.MUtils;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSource;
import okio.Okio;

/**
 * TS的下载采用多线程的下载，但是因为TS的分片过多，大则size>500以上，
 * 所以这里的多线程是把TS的list分割成 TS_RUNABLE_COUNT部分去下载，及保证多线程，也保证暂停时候可以控制，暂停控制采用
 */
public class DownloadTsService extends Service implements OnDownloadTsListener {
    private Handler mDownloadHandler;
    private List<IDownloadCallback> mWeakReferentCall = new ArrayList<>();
    private final byte[] lockList = new byte[0];

    //private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = 2;
    private static final int MAX_POOL_SIZE = 6;
    private static final long KEEP_ALIVE_TIME = 0L;

    private ThreadPoolExecutor threadPool;

    private SparseArray<List<DownloadTask>> taskArray = new SparseArray<>();
    private int TS_MAX_CUT_COUNT = 11;
    private int TS_CUT_RUNABLE_COUNT = 3;

    @Override
    public void onCreate() {
        super.onCreate();
        mDownloadHandler = new DownloadHandler(this, Downloader.getLooper());
        threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        parseIntent(intent);
        return START_STICKY;
    }

    /**
     * 执行intent
     *
     * @param intent
     */
    private void parseIntent(Intent intent) {
        if (null != intent && intent.hasExtra(DownloadConstats.KEY.SERVICE_INTENT_EXTRA)) {
            try {
                RequestInfo requestInfo = intent.getParcelableExtra(DownloadConstats.KEY.SERVICE_INTENT_EXTRA);
                if (requestInfo != null) {
                    if (requestInfo.requestStats == DownloadConstats.Request.START) {
                        excuteStart(requestInfo);
                    } else if (requestInfo.requestStats == DownloadConstats.Request.RESUME) {
                        excuteResume(requestInfo);
                    } else if (requestInfo.requestStats == DownloadConstats.Request.PAUSE) {
                        excutePause(requestInfo);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (null != intent && intent.hasExtra(DownloadConstats.KEY.SERVICE_INTENT_EXTRA_LIST)) {
            List<RequestInfo> requestInfos = intent.getParcelableArrayListExtra(DownloadConstats.KEY.SERVICE_INTENT_EXTRA_LIST);
            if (requestInfos != null && !requestInfos.isEmpty()) {
                excuteRefresh(requestInfos);
            }
        }
    }

    /**
     * 开始下载
     *
     * @param requestInfo
     */
    private void excuteStart(final RequestInfo requestInfo) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                // 通信Start
                mDownloadHandler.sendMessage(mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_START, requestInfo.m3u8url));

                // 解析
                M3U8 m3U8 = parseIndex(requestInfo.m3u8url);
                if (m3U8 != null) { // 保存状态信息
                    Log.d("ttt", "parse---" + m3U8.toString());
                    mDownloadHandler.sendMessage(mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_WAIT, requestInfo.m3u8url));

                    // M3u8Dao 保存m3u8数据库
                    M3u8Dao m3u8Dao = DownloadM3u8DB.getInstance(getBaseContext()).m3u8Dao();
                    String name = requestInfo.name;
                    String url = requestInfo.m3u8url;
                    String localPath = requestInfo.m3u8Path;
                    int state = DownloadConstats.STATE.STATE_M3U8_DEFAULT;
                    int tsSize = m3U8.getTsList().size();
                    int hasDownSize = 0;
                    String currentTs = "";
                    // JNI 获取帧图片
                    String imagePath = "";
                    long startTime = m3U8.getStartTime();
                    long endTime = m3U8.getEndTime();
                    long time = m3U8.getAllTime();
                    long hasDownTime = 0;
                    long startDownloadTime = System.currentTimeMillis() / 1000;
                    long endDownloadTime = System.currentTimeMillis() / 1000;
                    M3u8Model m3u8Model = new M3u8Model(name, url, localPath, state, tsSize, hasDownSize, currentTs, imagePath, startTime, endTime, time, hasDownTime, startDownloadTime, endDownloadTime);
                    long m3u8Id = m3u8Dao.insert(m3u8Model)[0];
                    requestInfo.m3u8Id = m3u8Id;

                    // TsDao 保存ts数据库
                    TsDao tsDao = DownloadM3u8DB.getInstance(getBaseContext()).tsDao();
                    String tsBasePath = requestInfo.basePath;
                    File file = new File(tsBasePath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    TsModel[] tsModels = new TsModel[tsSize];
                    for (int i = 0; i < tsSize; i++) {
                        M3U8Ts ts = m3U8.getTsList().get(i);
                        String m3u8Url = requestInfo.m3u8url;
                        String tsUrl = ts.getUrl().contains("http") ? ts.getUrl() : m3U8.getBasepath() + ts.getUrl();
                        String tsLocalPath = tsBasePath + File.separator + ts.getFileName();
                        int stateTs = DownloadConstats.STATE.STATE_TS_DEFAULT;
                        int timeTs = (int) ts.getSeconds();
                        TsModel tsModel = new TsModel(m3u8Url, tsUrl, tsLocalPath, m3u8Id, stateTs, timeTs);
                        tsModels[i] = tsModel;
                    }
                    tsDao.insert(tsModels);

                    mDownloadHandler.sendMessage(mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_START_DOWNLOAD, requestInfo));
                } else { // 保存为错误状态

                    mDownloadHandler.sendMessage(mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_FAILER, requestInfo.m3u8url));
                }
            }
        });
    }

    /**
     * 暂停下载
     */
    private void excutePause(RequestInfo requestInfo) {
        mDownloadHandler.sendMessage(mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_START_PAUSE, requestInfo));
    }

    /**
     * 恢复下载
     */
    private void excuteResume(RequestInfo requestInfo) {
        mDownloadHandler.sendMessage(mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_START_DOWNLOAD, requestInfo));
    }

    /**
     * 更新状态
     *
     * @param requestInfos
     */
    private void excuteRefresh(List<RequestInfo> requestInfos) {
        for (RequestInfo requestInfo : requestInfos) {
            if (requestInfo.requestStats == DownloadConstats.Request.REFRESH) {
                if (requestInfo.state == DownloadConstats.STATE.STATE_M3U8_DEFAULT
                        || requestInfo.state == DownloadConstats.STATE.STATE_M3U8_DOWNLADING) {
                    excuteResume(requestInfo);
                }
            } else if (requestInfo.requestStats == DownloadConstats.Request.RESUME) {
                if (requestInfo.state == DownloadConstats.STATE.STATE_M3U8_PAUSE) {
                    excuteResume(requestInfo);
                }
            } else if (requestInfo.requestStats == DownloadConstats.Request.PAUSE) {
                if (requestInfo.state != DownloadConstats.STATE.STATE_M3U8_SUCCESS
                        && requestInfo.state != DownloadConstats.STATE.STATE_M3U8_PAUSE) {
                    excutePause(requestInfo);
                }
            }
        }
    }

    // 下载模块，多线程，IPC通信，对象池使用
    private void download(final RequestInfo requestInfo) {
        Log.d("ttt", "download");
        final long m3u8Id = requestInfo.m3u8Id;
        final List<DownloadTask> downloadTasks = taskArray.get((int) m3u8Id, new ArrayList<DownloadTask>());
        if (downloadTasks.isEmpty() || requestInfo.state == DownloadConstats.STATE.STATE_M3U8_PAUSE) { // 已经有正在下载的情况下
            DataBaser.runDb(new DbAction() {
                @Override
                public Object action() {
                    return DownloadM3u8DB.getInstance(getApplicationContext()).tsDao().getTsFromM3u8NotSuccess(m3u8Id, DownloadConstats.STATE.STATE_TS_SUCCESS);
                }
            }, new DbCallback() {
                @Override
                public Object back(Object result) {
                    Message message = mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_DOWNING, requestInfo.process, 0, requestInfo.m3u8url);
                    mDownloadHandler.sendMessage(message);
                    DownloadM3u8DB.getInstance(getApplicationContext()).m3u8Dao().updateM3u8State(DownloadConstats.STATE.STATE_M3U8_DOWNLADING, m3u8Id);
                    List<TsModel> tsModels = (List<TsModel>) result;
                    if (!tsModels.isEmpty()) {
                        int tsAllSize = tsModels.size();
                        Log.d("ttt1", tsAllSize + "----tsAllSize");
                        if (tsModels.size() > TS_MAX_CUT_COUNT) { // 大于11
                            int everySize = tsModels.size() / TS_CUT_RUNABLE_COUNT;
                            int endSize = tsModels.size() % TS_CUT_RUNABLE_COUNT;
                            for (int i = 0; i < TS_CUT_RUNABLE_COUNT; i++) {
                                List<TsModel> tsModelsTs = new ArrayList<>(tsModels.subList(i * everySize, ((i + 1) * everySize)));
                                DownloadTask taskTs = new DownloadTask(m3u8Id, requestInfo.m3u8url, tsModelsTs);
                                taskTs.setDownloadListener(DownloadTsService.this);
                                downloadTasks.add(taskTs);
                                threadPool.execute(taskTs);
                            }
                            if (endSize > 0) {
                                List<TsModel> tsModelsTs = new ArrayList<>(tsModels.subList(TS_CUT_RUNABLE_COUNT * everySize, TS_CUT_RUNABLE_COUNT * everySize + endSize));
                                DownloadTask taskTs = new DownloadTask(m3u8Id, requestInfo.m3u8url, tsModelsTs);
                                taskTs.setDownloadListener(DownloadTsService.this);
                                downloadTasks.add(taskTs);
                                threadPool.execute(taskTs);
                            }

                            taskArray.put((int) m3u8Id, downloadTasks);
                        } else {
                            DownloadTask task = new DownloadTask(m3u8Id, requestInfo.m3u8url, tsModels);
                            task.setDownloadListener(DownloadTsService.this);

                            downloadTasks.add(task);
                            taskArray.put((int) m3u8Id, downloadTasks);

                            threadPool.execute(task);
                        }
                    }
                    return true;
                }
            });
        }
    }

    // 暂停下载
    private void pause(RequestInfo requestInfo) {
        final long m3u8Id = requestInfo.m3u8Id;
        List<DownloadTask> downloadTasks = taskArray.get((int) m3u8Id);
        for (DownloadTask downloadTask : downloadTasks) {
            downloadTask.setTsPause(true);
        }
        DataBaser.runDb(new DbAction() {
            @Override
            public Object action() {
                return DownloadM3u8DB.getInstance(getApplicationContext()).m3u8Dao().updateM3u8State(DownloadConstats.STATE.STATE_M3U8_PAUSE, m3u8Id);
            }
        });
    }

    /**
     * IPC通信的Handler，防止service的内存泄漏
     */
    private static class DownloadHandler extends Handler {
        private WeakReference<DownloadTsService> serviceWeakReference;

        public DownloadHandler(DownloadTsService service, Looper looper) {
            super(looper);
            serviceWeakReference = new WeakReference<>(service);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.d("ttt", "handleMessage--" + msg.what);
            DownloadTsService downloadTsService = serviceWeakReference.get();

            if (downloadTsService != null) {
                if (msg.what == DownloadConstats.MSG.MSG_START_DOWNLOAD) {
                    downloadTsService.download((RequestInfo) msg.obj);
                } else if (msg.what == DownloadConstats.MSG.MSG_START_PAUSE) {
                    downloadTsService.pause((RequestInfo) msg.obj);
                } else {
                    // IPC回调
                    List<IDownloadCallback> mWeakCall = downloadTsService.mWeakReferentCall;
                    for (IDownloadCallback iDownloadCallback : mWeakCall) {
                        Log.d("ttt", "iDownloadCallback ---" + (iDownloadCallback != null));
                        if (iDownloadCallback != null) {
                            try {
                                if (msg.what == DownloadConstats.MSG.MSG_START) { // 开始
                                    iDownloadCallback.onStart((String) msg.obj);
                                } else if (msg.what == DownloadConstats.MSG.MSG_WAIT) { // 等待
                                    iDownloadCallback.onWait((String) msg.obj);
                                } else if (msg.what == DownloadConstats.MSG.MSG_RESUME) { // 恢复下载
                                    iDownloadCallback.onResume((String) msg.obj);
                                } else if (msg.what == DownloadConstats.MSG.MSG_DOWNING) {
                                    iDownloadCallback.onProcess((String) msg.obj, msg.arg1);
                                } else if (msg.what == DownloadConstats.MSG.MSG_SUCCESS) {
                                    iDownloadCallback.onComplete((String) msg.obj);
                                } else if (msg.what == DownloadConstats.MSG.MSG_FAILER) {
                                    iDownloadCallback.onError((String) msg.obj);
                                } else if (msg.what == DownloadConstats.MSG.MSG_PAUSE) {
                                    iDownloadCallback.onPause((String) msg.obj);
                                }
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 返回IPC的服务器通信渠道
     */
    private IDownloadService mIDownloadService = new IDownloadService.Stub() {

        @Override
        public void addDownloadCallback(IDownloadCallback downloadCallback) throws RemoteException {
            synchronized (lockList) {
                if (!isHasDownloadCallback(downloadCallback)) {
                    mWeakReferentCall.add(downloadCallback);
                }
            }
        }

        private boolean isHasDownloadCallback(IDownloadCallback downloadCallback) {
            for (IDownloadCallback iDownloadCallback : mWeakReferentCall) {
                if (iDownloadCallback != null) {
                    if (iDownloadCallback.equals(downloadCallback)) {
                        return true;
                    }
                }
            }
            return false;
        }

        @Override
        public void removeDownloadCallback(IDownloadCallback downloadCallback) throws RemoteException {
            synchronized (lockList) {
                IDownloadCallback mWeakReference = null;
                for (IDownloadCallback iDownloadCallback : mWeakReferentCall) {
                    if (iDownloadCallback != null) {
                        if (iDownloadCallback.equals(downloadCallback)) {
                            mWeakReference = iDownloadCallback;
                        }
                    }
                }
                if (mWeakReference != null) {
                    mWeakReferentCall.remove(mWeakReference);
                }
            }
        }

        @Override
        public void clear() throws RemoteException {
            synchronized (lockList) {
                mWeakReferentCall.clear();
            }
        }

        @Override
        public void release() throws RemoteException {

        }
    };

    /**
     * 清空所有事件，防止内存泄漏
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        mWeakReferentCall.clear();
        taskArray.clear();
        mDownloadHandler.removeCallbacksAndMessages(null);
        DataBaser.release();
        DownloadM3u8DB.getInstance(getApplicationContext()).closeDb();
        Downloader.release();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mIDownloadService.asBinder();
    }

    /**
     * m3u8的链接解析
     *
     * @param m3u8url
     * @return
     */
    private M3U8 parseIndex(String m3u8url) {
        Response response = null;
        BufferedSource bufferedSource = null;
        try {
            OkHttpClient okHttpClient = OKhttper.getOkhttpClient();
            Request request = new Request.Builder().url(m3u8url).build();
            response = okHttpClient.newCall(request).execute();
            if (response != null && response.isSuccessful()) {
                ResponseBody body = response.body();
                if (body != null) {
                    String basepath = m3u8url.substring(0, m3u8url.lastIndexOf("/") + 1);
                    M3U8 m3U8 = new M3U8();
                    m3U8.setBasepath(basepath);
                    bufferedSource = Okio.buffer(Okio.source(body.byteStream()));
                    String line;
                    float seconds = 0;
                    while ((line = bufferedSource.readUtf8Line()) != null) {
                        if (line.startsWith("#")) {
                            if (line.startsWith("#EXTINF:")) {
                                line = line.substring(8);
                                if (line.lastIndexOf(",") != -1) {
                                    line = line.substring(0, line.lastIndexOf(","));
                                }
                                seconds = Float.parseFloat(line);
                            }
                            continue;
                        }
                        if (line.endsWith("m3u8")) {
                            return parseIndex(basepath + line);
                        }
                        m3U8.addTs(new M3U8Ts(line, seconds));
                        seconds = 0;
                    }
                    return m3U8;
                }
            }
        } catch (IOException e) {
            mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_FAILER, m3u8url);
        } finally {
            if (response != null) {
                response.close();
            }
            if (bufferedSource != null) {
                try {
                    bufferedSource.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }


    /**
     * ==============Ts下载监听======================
     */
    @Override
    public void onTsStart(final long tsId, String url) {
        Log.d("ttt", "onTsStart---" + tsId + "---" + url);
        // 更新数据库状态
        DataBaser.runDb(new DbAction() {
            @Override
            public Object action() {
                return DownloadM3u8DB.getInstance(getApplicationContext()).tsDao().updateTsState(DownloadConstats.STATE.STATE_TS_DOWNLADING, tsId);
            }
        });
    }

    @Override
    public void onTsComplete(final long tsId) {
        Log.d("ttt", "onTsComplete--" + tsId);
        // 更新数据库状态
        DataBaser.runDb(new DbAction() {
            @Override
            public Object action() {
                return DownloadM3u8DB.getInstance(getApplicationContext()).tsDao().updateTsState(DownloadConstats.STATE.STATE_TS_SUCCESS, tsId);
            }
        });
    }

    @Override
    public synchronized void onProcess(final long m3u8Id, final String m3u8Url, final String tsurl) {
        DataBaser.runDb(new DbAction() {
            @Override
            public Object action() {
                return DownloadM3u8DB.getInstance(getApplicationContext()).m3u8Dao().updateM3u8TsInfo(m3u8Id, tsurl);
            }
        });
        DataBaser.runDb(new DbAction() {
            @Override
            public Object action() {
                return DownloadM3u8DB.getInstance(getApplicationContext()).tsDao().getTsProcess(m3u8Id, DownloadConstats.STATE.STATE_TS_SUCCESS);
            }
        }, new DbCallback() {
            @Override
            public Object back(Object result) {
                int process = (int) result;
                mDownloadHandler.sendMessage(mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_DOWNING, process, 0, m3u8Url));
                return true;
            }
        });

    }

    @Override
    public void onTsError(final long tsId, String url) {
        Log.d("ttt", "onTsError");
        // 更新数据库状态
        DataBaser.runDb(new DbAction() {
            @Override
            public Object action() {
                return DownloadM3u8DB.getInstance(getApplicationContext()).tsDao().updateTsState(DownloadConstats.STATE.STATE_TS_FAIL, tsId);
            }
        });
    }

    @Override
    public synchronized void onTaskComplete(final long m3u8Id, Runnable runnable) {
        Log.d("ttt", "onTaskComplete");
        DataBaser.runDb(new DbAction() {
            @Override
            public Object action() {
                return DownloadM3u8DB.getInstance(getApplicationContext()).m3u8Dao().queryAllById(m3u8Id);
            }
        }, new DbCallback() {
            @Override
            public Object back(Object result) {
                List<M3u8Model> models = (List<M3u8Model>) result;
                if (models.isEmpty()) {
                    return null;
                }
                M3u8Model m3u8Model = models.get(0);
                if (m3u8Model.getHasDownSize() == m3u8Model.getTsSize()) {
                    Log.d("ttt", "download M3u8 success");
                    List<TsModel> tsModels = DownloadM3u8DB.getInstance(getApplicationContext()).tsDao().getTsFromM3u8(m3u8Id);
                    List<String> filePaths = new ArrayList<>();
                    for (TsModel tsModel : tsModels) {
                        filePaths.add(tsModel.getLocalPath());
                    }
                    MUtils.mergePath(filePaths, m3u8Model.getLocalPath());
                    return m3u8Model.getUrl();
                } else {
                    List<DownloadTask> tasks = taskArray.get((int) m3u8Id);
                    if (tasks.isEmpty()) {
                        DownloadM3u8DB.getInstance(getApplicationContext()).m3u8Dao().updateM3u8State(DownloadConstats.STATE.STATE_M3U8_PAUSE, m3u8Id);
                        mDownloadHandler.sendMessage(mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_PAUSE, m3u8Model.getUrl()));
                    }
                    return null;
                }
            }
        }, new DbCallback() {
            @Override
            public Object back(Object result) {
                if (result != null) {
                    DownloadM3u8DB.getInstance(getApplicationContext()).m3u8Dao().updateM3u8State(DownloadConstats.STATE.STATE_M3U8_SUCCESS, m3u8Id);
                    mDownloadHandler.sendMessage(mDownloadHandler.obtainMessage(DownloadConstats.MSG.MSG_SUCCESS, result));
                }
                return true;
            }
        });
        List<DownloadTask> tasks = taskArray.get((int) m3u8Id);
        tasks.remove(runnable);
        taskArray.put((int) m3u8Id, tasks);
    }
}
